package com.jasic.comnt.conn;

import com.jasic.common.GlobalConstant;
import com.jasic.common.exception.HandleException;
import com.jasic.util.ByteUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * 协议的socket处理类
 */
public class PostSrvSocket {

    // 日志处理
    private static final Logger logger = LoggerFactory.getLogger(PostSrvSocket.class);

    // 客户端socket连接
    private Socket handleSocket = null;

    // 输入流
    private InputStream inputStream = null;

    // 输出流
    private OutputStream outputStream = null;

    // socket数据打印标志
    public boolean socketLogFlag = false;

    // 发送超时时间,单位(秒)
    private int lingerTime = 0;

    // 接收超时时间,单位(毫秒)
    private int socketTimeout = 0;

    // 缓冲区大小
    private int bufferSize = 1024 * 1024;

    // 客户端地址
    private String localAddress = "";

    // 服务端地址
    private String serverAddress = "";


    /**
     * 构造函数，服务端的socket处理实体
     */
    public PostSrvSocket(Socket handleSocket) {
        this.handleSocket = handleSocket;

        try {
            if (this.handleSocket != null) {
                initServer();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            logger.info("Exception: ", e);
        }
    }

    /**
     * 初始化服务端的socket
     *
     * @throws java.io.IOException
     */
    private void initServer() throws IOException {

        // 初始化连接参数
        initParam();

        // 初始化数据流
        initStream();
    }

    /**
     * 构造函数,客户端的socket处理实体
     *
     * @param ip
     * @param port
     *
     * @throws java.io.IOException
     */
    public PostSrvSocket(String ip, int port) throws IOException {

        this.socketTimeout = 0;
        initClient(new InetSocketAddress(ip, port));
    }

    /**
     * 构造函数,客户端的socket处理实体
     *
     * @throws java.io.IOException
     */
    public PostSrvSocket(InetSocketAddress address) throws IOException {

        this.socketTimeout = 0;
        initClient(address);
    }

    /**
     * 构造函数,客户端的socket处理实体
     *
     * @param ip
     * @param port
     *
     * @throws java.io.IOException
     */
    public PostSrvSocket(String ip, int port, int timeout) throws IOException {
        this.socketTimeout = timeout;

        initClient(new InetSocketAddress(ip, port));
    }

    /**
     * 初始化参数
     *
     * @throws java.io.IOException
     */
    private void initParam() throws IOException {

        // 设置发送超时时间
        handleSocket.setSoLinger(true, lingerTime);

        // 设置InputStream上调用 read()阻塞超时时间,单位(毫秒)
        if (socketTimeout > 0)
            handleSocket.setSoTimeout(socketTimeout);

        // 设置handle_socket发包缓冲；
        handleSocket.setSendBufferSize(bufferSize);

        // 设置handle_socket底层接收缓冲
        handleSocket.setReceiveBufferSize(bufferSize);

        // 关闭Nagle算法.立即发包
        //handleSocket.setTcpNoDelay(true);

        // 设定连接时间、延迟和带宽的相对重要性
        // handleSocket.setPerformancePreferences(1, 0, 2);

        // 高可靠性和最小延迟传输
        // handleSocket.setTrafficClass(0x04 | 0x10);
    }

    /**
     * 初始化数据流
     *
     * @throws java.io.IOException
     */
    private void initStream() throws IOException {

        // 输入流
        inputStream = handleSocket.getInputStream();

        // 输出流
        outputStream = handleSocket.getOutputStream();

        // 客户端地址
        localAddress = handleSocket.getLocalAddress().getHostAddress() + ":" + handleSocket.getLocalPort();

        // 服务端地址
        serverAddress = handleSocket.getInetAddress().getHostAddress() + ":" + handleSocket.getPort();
    }

    /**
     * 初始化客户端socket连接
     *
     * @param ip
     * @param port
     *
     * @throws java.io.IOException
     */
    private void initClient(InetSocketAddress address) throws IOException {

        // 建立实例
        handleSocket = new Socket();

        // 初始化连接参数
        initParam();

        // 连接服务器
        handleSocket.connect(address);

        // 初始化数据流
        initStream();

    }

    /**
     * 调用服务,客户端使用
     *
     * @param data
     *
     * @return
     *
     * @throws HandleException
     * @throws java.io.IOException
     * @throws Exception
     */
    public byte[] call(byte[] data) throws HandleException, IOException, Exception {
        send(data);
        return recv();
    }

    /**
     * 返回结果数据
     *
     * @param handleSocket
     * @param data
     *
     * @throws HandleException, IOException, Exception
     */
    public void send(byte[] data) throws IOException {

        // 输出socket数据日志
        if (socketLogFlag) {
            logger.info("[" + localAddress + "->" + serverAddress + "][" + ByteUtil.toHexString(data) + "]");
        }

        // 每次写入长度，如数据包剩余数据长度少于dataUnit则使用剩余数据长度
        int dataUnit = 1024;
        int writeLen = dataUnit;

        // 已经读取的数据包数据长度
        int bodyWriteLen = 0;

        // 需要读取的数据包长度
        int bodyTotalLen = data.length;

        synchronized (handleSocket) {

            while (true) {
                if ((writeLen = bodyTotalLen - bodyWriteLen) >= dataUnit) {
                    writeLen = dataUnit;
                }

                try {
                    outputStream.write(data, bodyWriteLen, writeLen);
                } catch (IOException e) {
                    this.close();
                    throw e;
                }

                bodyWriteLen += writeLen;

                if (bodyWriteLen >= bodyTotalLen)
                    break;
            }

            outputStream.flush();
        }
    }

    /**
     * 接收数据
     *
     * @throws HandleException
     * @throws java.io.IOException
     * @throws Exception
     */
    public byte[] recv() throws HandleException, IOException, Exception {

        // 读取的数据包头
        int readHeadLen = 0;

        byte[] tmpByte = new byte[1];

        boolean findHeader = false;
        boolean findTail = false;

        //每一条消息的临时载体
        byte[] tempContent = new byte[1024];
        // tempContent被copy的位置
        int pos = 0;

        while (!findHeader || !findTail) {

            readHeadLen = inputStream.read(tmpByte, 0, 1);
            if (readHeadLen <= 0) {
                this.close();
                throw new HandleException("0000", "连接被断开");
            }
            // 如果为标志头，解释下一步，否则一直寻找标志头
            if (!findHeader && tmpByte[0] == GlobalConstant.HEADER) {
                findHeader = true;
                System.arraycopy(tmpByte, 0, tempContent, pos++, tmpByte.length);
                continue;
            }

            // 如果为标志尾，解释下一步，否则一直寻找标志尾
            if (!findTail && tmpByte[0] == GlobalConstant.TAIL) {

                findTail = true;
                System.arraycopy(tmpByte, 0, tempContent, pos++, tmpByte.length);
                if (pos != tempContent[1]) {
                    throw new RuntimeException("数据包长度,与实际不一至");
                }

                byte[] result = new byte[pos];
                System.arraycopy(tempContent, 0, result, 0, result.length);
                return result;
            }

            // 只有找到头才增加位置
            if (findHeader) {
                System.arraycopy(tmpByte, 0, tempContent, pos++, tmpByte.length);
                if (pos > tempContent.length) {
                    throw new HandleException("Exceed length", "数据包长度有误，超出长度...");
                }
            }

        }

        return new byte[0];
    }


    /**
     * 关闭socket
     */
    public void close() {
        if (outputStream != null) {
            try {
                outputStream.close();
            } catch (Exception e) {
                logger.info("Exception: ", e);
            }
        }

        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (Exception e) {
                logger.info("Exception: ", e);
            }
        }

        if (handleSocket != null) {
            try {
                handleSocket.close();
            } catch (Exception e) {
                logger.info("Exception: ", e);
            }
        }
    }

    /**
     * 当前的socket连接是否有效
     *
     * @return
     */
    public boolean isConnected() {
        return this.handleSocket.isConnected() && !this.handleSocket.isClosed()
                && !this.handleSocket.isInputShutdown() && !this.handleSocket.isOutputShutdown();
    }

    public Socket getHandleSocket() {
        return handleSocket;
    }

    public void setHandleSocket(Socket handleSocket) {
        this.handleSocket = handleSocket;
    }
}
